package com.wk.springboot.httpUtils;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeUnit;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.config.SocketConfig;
import org.apache.http.conn.DnsResolver;
import org.apache.http.conn.HttpConnectionFactory;
import org.apache.http.conn.ManagedHttpClientConnection;
import org.apache.http.conn.routing.HttpRoute;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.DefaultConnectionReuseStrategy;
import org.apache.http.impl.client.DefaultConnectionKeepAliveStrategy;
import org.apache.http.impl.client.DefaultHttpRequestRetryHandler;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.DefaultHttpResponseParserFactory;
import org.apache.http.impl.conn.ManagedHttpClientConnectionFactory;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.impl.conn.SystemDefaultDnsResolver;
import org.apache.http.impl.io.DefaultHttpRequestWriterFactory;

/**
 * @Classname HttpClientFactory
 * @Description httpClient的生产工厂
 * @Date 2020/8/7 11:13
 * @Author by weikai
 */
public class HttpClientFactory {
  // 最大连接数默认100
  private static final int MAX_TOTAL = 100;

  private static final int MAX_PER_ROUTE = 200;

  /**
   * 连接超时时间 默认20秒
   */
  private static final int CONNECTION_TIMEOUT = 20000;

  /**
   * 设置等待数据超时时间600000 默认10分钟未返回结果就报错
   */
  private static final int SOCKET_TIMEOUT = 600000;

  /**
   * 从连接池获取连接的等待超时时间 默认3000 3秒
   */
  private static final int REQUEST_TIMEOUT = 3000;

  /**
   * 连接不活跃多长时间进行一次验证，单位：ms
   */
  private static final int VALIDATE_AFTER_INACTIVITY = 5000;

  /**
   * 定期回收空闲时间，单位：s
   */
  private static final int EVICT_IDLE_CONNECTION = 60;

  /**
   * 连接存活时间，单位：s
   */
  private static final int CONNECTION_TIMETOLIVE = 360;

  /**
   * 连接存活时间，单位：s
   */
  private static final int RETRY = 0;

  /**
   * 缓存所有在程序运行过程中产生的httpclient
   */
  private static final List<DrspHttpClient> HTTP_CLIENT_LIST = Collections
      .synchronizedList(new ArrayList<DrspHttpClient>());

  private HttpClientFactory() {

  }

  /**
   * 创建自定义配置的httpclient
   * @param connectionTimeout 连接超时时间
   * @param socketTimeout     设置等待数据超时时间
   * @param requestTimeout    从连接池获取连接的等待超时时间
   * @return
   */
  public static DrspHttpClient createCustomClient(int connectionTimeout, int socketTimeout, int requestTimeout) {
    HttpClientBuilder builder = HttpClients.custom();
    builder.setMaxConnTotal(MAX_TOTAL);
    builder.setMaxConnPerRoute(MAX_PER_ROUTE);
    builder.setDefaultRequestConfig(getRequestConfig(connectionTimeout, socketTimeout, requestTimeout));
    DrspHttpClient httpClient = new DrspHttpClient(builder.build());
    HTTP_CLIENT_LIST.add(httpClient);
    return httpClient;

  }

  public static DrspHttpClient createCustomClient(int connectionTimeout, int socketTimeout, int requestTimeout,
      PoolingHttpClientConnectionManager manager) {
    HttpClientBuilder builder = HttpClients.custom();
    builder.setConnectionManager(manager);
    builder.setMaxConnTotal(MAX_TOTAL);
    builder.setMaxConnPerRoute(MAX_PER_ROUTE);
    builder.setDefaultRequestConfig(getRequestConfig(connectionTimeout, socketTimeout, requestTimeout));
    DrspHttpClient httpClient = new DrspHttpClient(builder.build());
    HTTP_CLIENT_LIST.add(httpClient);
    return httpClient;
  }

  /**
   * 创建默认的httpclient
   * 一次创建，多次使用，可以根据不同的业务场景创建不同的 client对象
   * @return
   * @author liufei
   * @time 2018-1-17上午10:33:38
   */
  public static DrspHttpClient createDefaultClient() {

    PoolingHttpClientConnectionManager manager = buildPoolConnManager();

    HttpClientBuilder builder = HttpClients.custom()//
        .setConnectionManager(manager).setConnectionManagerShared(false)//连接池不是共享模式
        .evictIdleConnections(EVICT_IDLE_CONNECTION, TimeUnit.SECONDS)//定期回收空闲连接
        .evictExpiredConnections()//定期回收过期连接
        .setConnectionTimeToLive(CONNECTION_TIMETOLIVE, TimeUnit.SECONDS)//连接存活时间，如果不设置，则根据长连接信息决定
        .setConnectionReuseStrategy(DefaultConnectionReuseStrategy.INSTANCE)//连接重用策略，即是否能keepAlive
        .setKeepAliveStrategy(DefaultConnectionKeepAliveStrategy.INSTANCE)//长连接配置，即获取长连接生产多长时间
        .setRetryHandler(new DefaultHttpRequestRetryHandler(RETRY, false));//设置重试次数，默认是3次，当前是禁用掉

    builder.setMaxConnTotal(MAX_TOTAL);//设置整个连接池的最大连接数
    builder.setMaxConnPerRoute(MAX_PER_ROUTE);//
    builder.setDefaultRequestConfig(getRequestConfig());

    DrspHttpClient httpClient = new DrspHttpClient(builder.build());
    HTTP_CLIENT_LIST.add(httpClient);
    return httpClient;
  }

  /**
   * @return
   */
  private static RequestConfig getRequestConfig(int connectionTimeout, int socketTimeout, int requestTimeout) {

    return RequestConfig.custom().setConnectTimeout(connectionTimeout != 0 ? connectionTimeout : CONNECTION_TIMEOUT)
        .setSocketTimeout(connectionTimeout != 0 ? socketTimeout : SOCKET_TIMEOUT)
        .setConnectionRequestTimeout(requestTimeout != 0 ? requestTimeout : REQUEST_TIMEOUT).build();

  }

  private static RequestConfig getRequestConfig() {
    // 默认请求配置
    return RequestConfig.custom().setConnectTimeout(CONNECTION_TIMEOUT).setSocketTimeout(SOCKET_TIMEOUT)
        .setConnectionRequestTimeout(REQUEST_TIMEOUT).build();
  }

  /**
   * @return
   *
   */
  private static PoolingHttpClientConnectionManager buildPoolConnManager() {
    // 注册访问协议相关的socket工厂
    Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory> create()
        .register("http", PlainConnectionSocketFactory.INSTANCE)
        .register("https", SSLConnectionSocketFactory.getSystemSocketFactory()).build();

    // HttpConnection 工厂：配置写请求/解析响应处理器
    HttpConnectionFactory<HttpRoute, ManagedHttpClientConnection> connFactory = new ManagedHttpClientConnectionFactory(
        DefaultHttpRequestWriterFactory.INSTANCE, DefaultHttpResponseParserFactory.INSTANCE);

    // DNS解析器
    DnsResolver dnsResolver = SystemDefaultDnsResolver.INSTANCE;

    // 创建池化连接管理器
    PoolingHttpClientConnectionManager manager = new PoolingHttpClientConnectionManager(socketFactoryRegistry, connFactory, dnsResolver);

    //默认为socket配置
    SocketConfig defaultSocketConfig = SocketConfig.custom().setTcpNoDelay(true).build();
    manager.setDefaultSocketConfig(defaultSocketConfig);

    manager.setMaxTotal(MAX_TOTAL);
    manager.setDefaultMaxPerRoute(MAX_PER_ROUTE);
    manager.setValidateAfterInactivity(VALIDATE_AFTER_INACTIVITY);
    return manager;
  }

  static {
    // JVM停止或启动时，关闭连接池释放掉连接
    Runtime.getRuntime().addShutdownHook(new Thread() {
      @Override
      public void run() {
        for (IHttpClient httpClient : HTTP_CLIENT_LIST) {
          httpClient.close();
        }
      }
    });
  }
}
